namespace Antivirus.Domain.Logic.Scanners
{
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Threading;
    using Hashers;

    public class DefaultScanner : IScanner
    {
        public event FileScanCompletedEventHandler FileScanCompleted;
        public event BatchScanCompletedEventHandler BatchScanCompleted;

        private readonly IHasher _hasher;

        private readonly IEnumerable<VirusSignature> _virusSignatures;

        private Thread _scanThread;

        public DefaultScanner(IHasher hasher, IEnumerable<VirusSignature> virusSignatures)
        {
            _hasher = hasher;
            _virusSignatures = virusSignatures;
        }

        public void ScanFile(string filename)
        {
            if (_scanThread != null && _scanThread.IsAlive)
                throw new ScanInProgressException();

            _scanThread = new Thread(() => ScanFileSync(filename, 1));

            var monitorThread = new Thread(() =>
                                               {
                                                   _scanThread.Start();
                                                   _scanThread.Join();

                                                   OnBatchScanCompleted(new BatchScanCompletedEventArgs(filename, 1));
                                               });
            monitorThread.Start();
        }

        public void ScanDirectory(string directory, bool isRecursive)
        {
            if (_scanThread != null && _scanThread.IsAlive)
                throw new ScanInProgressException();

            var totalFilesCount = 0;

            _scanThread = new Thread(() =>
                                         {
                                             var searchOption = isRecursive
                                                                    ? SearchOption.AllDirectories
                                                                    : SearchOption.TopDirectoryOnly;
                                             var files = Directory.EnumerateFiles(directory, "*.*", searchOption)
                                                 .ToArray();

                                             totalFilesCount = files.Length;

                                             foreach (var file in files)
                                                 ScanFileSync(file, files.Length);
                                         });

            var monitorThread = new Thread(() =>
                                               {
                                                   _scanThread.Start();
                                                   _scanThread.Join();

                                                   OnBatchScanCompleted(new BatchScanCompletedEventArgs(directory, totalFilesCount));
                                               });
            monitorThread.Start();
        }

        public void Stop()
        {
            if (_scanThread != null && _scanThread.IsAlive)
                _scanThread.Abort();
        }

        public void OnFileScanCompleted(FileScanCompletedEventArgs args)
        {
            var handler = FileScanCompleted;
            if (handler != null)
                handler(this, args);
        }

        public void OnBatchScanCompleted(BatchScanCompletedEventArgs args)
        {
            var handler = BatchScanCompleted;
            if (handler != null) 
                handler(this, args);
        }

        private void ScanFileSync(string filename, int totalFilesCount)
        {
            var hash = _hasher.Calculate(File.OpenRead(filename));

            var virusSignature = _virusSignatures
                .FirstOrDefault(x => x.Signature.Equals(hash));

            OnFileScanCompleted(new FileScanCompletedEventArgs(filename, virusSignature, totalFilesCount));
        }
    }
}                                 